﻿using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using log4net;
using PagedList;
using QAS.Nekorama.Common;
using QAS.Nekorama.Filters;
using QAS.Nekorama.Models;
using WebMatrix.WebData;
using Resources;

namespace QAS.Nekorama.Controllers
{
    /// <summary>
    /// ユーザーモデルのコントローラー
    /// ビュー含有:　Index.cshtml, Details.cshtml
    ///  アクション含有: Index, Details, BanUser, Edit, EditUserInfo
    /// </summary>
    [InitializeSimpleMembership]
    [HandleError]
    public class UserController : Controller
    {
        private readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private readonly Entities _db = new Entities();

        /// <summary>
        /// Indexビューからパラメーターをもらい、モデルに相応なデータを取り出ます。
        /// </summary>
        /// <param name="currentFilter">現在なフィルタストリング</param>
        /// <param name="searchUserString">検索キーワード</param>
        /// <param name="page">現在なページの番</param>
        /// <returns>Indexビュー</returns>
        public ActionResult Index(string currentFilter, string searchUserString, int? page)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            try
            {
                //ページング中に現在なフィルタに検索キーワードをします。そして、ページが再表示される時テキストボックスに検索キーワードを戻します。
                if (searchUserString != null)
                {
                    //新しい検索キーワードがビューから送られます。他の結果ページを表示するためページを１に再設定します。
                    searchUserString = searchUserString.Trim();
                    page = 1;
                }
                else
                {
                    //ページング
                    searchUserString = currentFilter;
                }
                ViewBag.CurrentFilter = searchUserString;

                //データベースからユーザーの情報を取り出ます。
                IQueryable<User> users = from u in _db.Users orderby u.DisplayName select u;

                //ユーザー名で探します。
                if (!String.IsNullOrEmpty(searchUserString))
                {
                    users = users.Where(u => u.DisplayName.Contains(searchUserString));
                }
                int pageNumber = (page ?? 1);
                _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);

                return View(users.ToPagedList(pageNumber, 30));
            }
            catch (ArgumentNullException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Index"));
            }
            catch (InvalidOperationException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Index"));
            }
            catch (Exception exception)
            {
                _log.Fatal(
                    string.Format("A fatal exception has occured at {0} action ", MethodBase.GetCurrentMethod().Name) +
                    exception.Message, exception);
                throw;
            }
        }

        /// <summary>
        ///  ユーザーの情報が表示されます。
        /// </summary>
        /// <param name="id">ユーザーのＩＤ</param>
        /// <param name="questionPage">現在な質問のページの番</param>
        /// <param name="answerPage">現在な回答のページの番</param>
        /// <returns>Detailビュー</returns>
        public ActionResult Details(int id, int? questionPage, int? answerPage)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            try
            {
                ViewBag.DateSortParm = "Date_desc";
                var userDetail = new UserModel();
                User user = _db.Users.SingleOrDefault(u => u.UserID == id);
                if (user==null)
                {
                    return View("Error",
                        new HandleErrorInfo(new Exception(Messages.USERID_DOES_NOT_EXIST), "User", "Details"));
                }
                IOrderedQueryable<Question> question =
                    _db.Questions.Where(b => b.CreatedUser == id)
                        .Where(b => b.IsDeleted != true && b.IsSpam != true)
                        .OrderByDescending(q => q.CreatedDate);
                IOrderedQueryable<Answer> answer =
                    _db.Answers.Where(b => b.CreatedUser == id)
                        .Where(b => b.IsDeleted != true && b.IsSpam != true)
                        .OrderByDescending(a => a.CreatedDate);
                int questionPageNumber = (questionPage ?? 1);
                int answerPageNumber = (answerPage ?? 1);
                userDetail.Questions = question.ToPagedList(questionPageNumber, 5);
                userDetail.Answers = answer.ToPagedList(answerPageNumber, 5);
                userDetail.Users = user;
                _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                return View(userDetail);
            }
            catch (ArgumentNullException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Details"));
            }
            catch (InvalidOperationException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Details"));
            }
            catch (Exception exception)
            {
                _log.Fatal(
                    string.Format("A fatal exception has occured at {0} action ", MethodBase.GetCurrentMethod().Name) +
                    exception.Message, exception);
                throw;
            }
        }

        /// <summary>
        ///  ユーザーを禁止します。
        /// </summary>
        /// <param name="userId">ユーザーのＩＤ</param>
        /// <returns>Detailsビュー</returns>
        public ActionResult BanUser(int userId)
        {
            int currentUser = WebSecurity.CurrentUserId;
            if (Helper.GetReputation(WebSecurity.CurrentUserName) >= (int)Constants.Privileges.Moderator)
            {
                try
                {
                    User user = _db.Users.First(u => u.UserID == userId);
                    user.BanDuration = user.BanDuration > 0 ? 0 : Constants.BAN_DURATION;
                    user.LastModifiedDate = DateTime.Now;
                    user.ModifiedUser = currentUser;
                    _db.SaveChanges();
                    _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                    return RedirectToAction("Details", new { userid = userId });
                }
                catch (ArgumentNullException exception)
                {
                    _log.Error(
                        string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                            MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                    return View("Error", new HandleErrorInfo(exception, "User", "BanUser"));
                }
                catch (InvalidOperationException exception)
                {
                    _log.Error(
                        string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                            MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                    return View("Error", new HandleErrorInfo(exception, "User", "BanUser"));
                }
                catch (Exception exception)
                {
                    _log.Fatal(
                        string.Format("A fatal exception has occured at {0} action ", MethodBase.GetCurrentMethod().Name) +
                        exception.Message, exception);
                    throw;
                }
            } else {
                return View("Error", new HandleErrorInfo(new Exception(Messages.NOT_ENOUGH_PERMISSION), "user", "BanUser"));
            }
        }

        /// <summary>
        /// ユーザーの情報を編集します。
        /// </summary>
        /// <param name="userId">ユーザーのID</param>
        /// <returns>Detailsビュー</returns>
        public ActionResult Edit(int userId)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            try
            {
                int currentUser = WebSecurity.CurrentUserId;
                if (User.Identity.IsAuthenticated && _db.Users.Find(currentUser).BanDuration == 0 && currentUser == userId)
                {
                    User user = _db.Users.Find(userId);
                    ViewData["realName"] = Session["realname"] == null ? user.FullName : Session["realname"] + "";
                    ViewData["aboutMe"] = Session["aboutme"] == null ? user.About : Session["aboutme"] + "";
                    ViewData["birthday"] = Session["birthday"] == null
                        ? String.Format("{0:yyy-MM-dd}", user.DateOfBirth)
                        : Session["birthday"] + "";
                    ViewData["location"] = Session["location"] == null ? user.Location : Session["location"] + "";
                    Session["realname"] = null;
                    Session["birthday"] = null;
                    Session["aboutMe"] = null;
                    Session["location"] = null;
                    _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                    return View(user);
                }else{
                    TempData["error"] = Messages.NOT_ENOUGH_PERMISSION;
                    _log.Error(string.Format("Don't have permission to edit profile"));
                    return RedirectToAction("Details", "User", new { id = WebSecurity.CurrentUserId, qPage = 1, aPage = 1 });
                }
            }
            catch (ArgumentNullException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Edit"));
            }
            catch
                (InvalidOperationException
                    exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "Edit"));
            }
            catch
                (Exception
                    exception)
            {
                _log.Fatal(
                    string.Format("A fatal exception has occured at {0} action ", MethodBase.GetCurrentMethod().Name) +
                    exception.Message, exception);
                throw;
            }
        }

        /// <summary>
        /// ユーザーの情報を編集します。
        /// </summary>
        /// <returns>Detailビュー</returns>
        [ValidateInput(false)]
        public ActionResult EditUserInfo()
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            try
            {
                int currentUser = WebSecurity.CurrentUserId;
                if (!User.Identity.IsAuthenticated || _db.Users.Find(currentUser).BanDuration != 0)
                {
                    _log.Error(string.Format("An error has occured at {0} action. User is not logged in or is banned.",
                        MethodBase.GetCurrentMethod().Name));
                    return null;
                }
                User user = _db.Users.First(u => u.UserID == currentUser);

                //ビューからデータを取り出します。
                string realName = (Request.Form["realname"] + "").Trim();
                DateTime birthday;
                if (Request.Form["datepicker"] != "")
                {
                    birthday = Convert.ToDateTime(Request.Form["datepicker"]);
                }
                else
                {
                    birthday = default(DateTime);
                }
                string location = (Request.Form["location"] + "").Trim();
                string aboutMe = (Request.Form["aboutme"] + "").Trim();
                Session["realname"] = realName;
                Session["aboutme"] = aboutMe;
                Session["birthday"] = Request.Form["datepicker"];
                Session["location"] = location;
                user.FullName = realName;
                if (Request.Form["datepicker"] != "")
                {
                    user.DateOfBirth = birthday;
                }
                user.Location = location;
                user.About = aboutMe;
                user.LastModifiedDate = DateTime.Now;
                user.ModifiedUser = currentUser;
                _db.SaveChanges();
                //編集は成功しました。
                Session["realname"] = null;
                Session["aboutme"] = null;
                Session["birthday"] = null;
                Session["location"] = null;
                _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                return RedirectToAction("Details", "User", new {id = currentUser, qPage = 1, aPage = 1});
            }
            catch (ArgumentNullException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "EditUserInfo"));
            }
            catch (InvalidOperationException exception)
            {
                _log.Error(
                    string.Format("An {0} exception has occured at {1} action. ", exception.GetType(),
                        MethodBase.GetCurrentMethod().Name) + exception.Message, exception);
                return View("Error", new HandleErrorInfo(exception, "User", "EditUserInfo"));
            }
            catch (Exception exception)
            {
                _log.Fatal(
                    string.Format("A fatal exception has occured at {0} action ", MethodBase.GetCurrentMethod().Name) +
                    exception.Message, exception);
                throw;
            }
        }
        /// <summary>
        /// 使用の後、エンティティのオブジェクトを削除します。
        /// </summary>
        /// <param name="disposing">削除するかどうか</param>
        protected override void Dispose(bool disposing)
        {
            _db.Dispose();
            base.Dispose(disposing);
        }
        /// <summary>
        /// アクションで処理されない例外を処理します。
        /// </summary>
        /// <param name="filterContext">例外のコンテキスト</param>
        protected override void OnException(ExceptionContext filterContext)
        {
            filterContext.Exception = new Exception("");
            filterContext.ExceptionHandled = true;
            filterContext.Result = View("Error", new HandleErrorInfo(filterContext.Exception, "User", "Details"));
            //If the exeption is already handled we do nothing
        }
    }
}